from pyglet.gl import *
import math
import noise
import game
import gfx

class GiantPlanet:

	slices = 50
	stacks = 12

	def __init__(self, radius, color=(1,1,1), tilt=0, rot_speed=0.5,
		bands=0, band_deviation=0):
		self.radius = float(radius)
		self.band_deviation = band_deviation
		self.tilt = tilt
		self.rot_speed = rot_speed
		self.color = color
		self.sphere_mesh = self.create_bands(12, 20, deviation=0)
		self.compile()
	
	def create_bands(self, stacks, slices, deviation=0):
		top_bands = []
		bottom_bands = []
		stack_h = self.radius / (stacks // 2)
		slice_arc = math.pi * 2.0 / slices
		stack_arc = math.pi / slices
		for stack in range(stacks // 2):
			theta = (math.pi * stack) / stacks
			y = stack_y = math.sin(theta) * self.radius
			while 1:
				band = []
				for slice in range(slices):
					angle = slice * slice_arc
					if deviation:
						stack_d = math.sqrt(self.radius**2 - stack_y**2) * 2.0
						t = noise.pnoise1(stack_d * slice / slices,
							repeat=stack_d * slices, 
							base=slices * stack_y,
							octaves=4) * deviation * stack_arc * 2.0
						y = min(self.radius, max(stack_y + math.sin(t) * self.radius, -self.radius))
					band_radius = math.sqrt(self.radius**2 - y**2)
					band.append(
						(math.cos(angle) * band_radius, y, math.sin(angle) * band_radius))
				if stack_y > 0:
					top_bands.append(band)
					y = stack_y = -stack_y
				elif stack_y == 0:
					top_bands.append(band)
					break
				else:
					bottom_bands.append(band)
					break
		top_bands.reverse()
		return top_bands + bottom_bands
	
	def compile(self):
		"""Compile the planet drawing commands into display lists"""
		'''
		self.edges_dl = gfx.DisplayList()
		self.edges_dl.begin()
		self.draw_edges()
		self.edges_dl.end()
		self.draw_edges = self.edges_dl.execute
		'''
		self.sphere_dl = gfx.DisplayList()
		self.sphere_dl.begin()
		self.draw_sphere(self.sphere_mesh)
		self.sphere_dl.end()

	def draw_edges(self):
		"""Draw the sphere bands centered at the origin"""
		return
		for band in self.bands:
			glBegin(GL_LINE_LOOP)
			for v in band:
				glVertex3f(*v)
			glEnd()
	
	def draw_sphere(self, bands):
		"""Draw sphere centered at the origin"""
		# Draw top cap
		glEnable(GL_NORMALIZE)
		glBegin(GL_TRIANGLE_FAN)
		glNormal3f(0, 1, 0)
		glVertex3f(0, self.radius, 0)
		for v in reversed(bands[0]):
			glNormal3f(*v)
			glVertex3f(*v)
		glNormal3f(*bands[0][-1])
		glVertex3f(*bands[0][-1])
		glEnd()
		# Draw middle bands
		slice_range = range(len(bands[0]))
		for stack in range(len(bands) - 1):
			glBegin(GL_QUAD_STRIP)
			for slice in slice_range:
				glNormal3f(*bands[stack + 1][slice])
				glVertex3f(*bands[stack + 1][slice])
				glNormal3f(*bands[stack][slice])
				glVertex3f(*bands[stack][slice])
			glNormal3f(*bands[stack + 1][0])
			glVertex3f(*bands[stack + 1][0])
			glNormal3f(*bands[stack][0])
			glVertex3f(*bands[stack][0])
			glEnd()
		# Draw bottom cap
		glBegin(GL_TRIANGLE_FAN)
		glNormal3f(0, -1, 0)
		glVertex3f(0, -self.radius, 0)
		for v in bands[-1]:
			glNormal3f(*v)
			glVertex3f(*v)
		glNormal3f(*bands[-1][0])
		glVertex3f(*bands[-1][0])
		glEnd()
		glDisable(GL_NORMALIZE)
	
	def draw(self):
		glPushMatrix()
		glPushAttrib(GL_DEPTH_BUFFER_BIT | GL_POLYGON_BIT)
		glRotatef(self.tilt, 0, 0, 1)
		glRotatef(self.rot_speed * game.time, 0, 1, 0)
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
		glEnable(GL_LIGHTING)
		glEnable(GL_COLOR_MATERIAL)
		glEnable(GL_DEPTH_TEST)
		glEnable(GL_CULL_FACE)
		r, g, b = self.color
		glColor4f(r*0.5, g*0.5, b*0.5, 1.0)
		self.sphere_dl.execute()
		glDisable(GL_LIGHTING)
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
		glDisable(GL_DEPTH_TEST)
		glDisable(GL_CULL_FACE)
		glColor4f(r, g, b, 0.015)
		glLineWidth(3)
		self.sphere_dl.execute()
		glEnable(GL_CULL_FACE)
		glLineWidth(2)
		glColor4f(r, g, b, 0.10)
		self.sphere_dl.execute()
		"""
		glLineWidth(1)
		glColor3f(*self.color)
		self.draw_edges()
		"""
		glPopAttrib()
		glPopMatrix()
		


class Ring:

	def __init__(self, inner_radius, outer_radius, slices):
		self.inner_radius = inner_radius
		self.outer_radius = outer_radius
		self.slices = slices
		self.create_verts()
	
	def create_verts(self):
		self.bands = []
		for radius in (self.outer_radius, self.inner_radius):
			band = []
			for slice in range(self.slices):
				angle = math.pi * 2.0 * slice / self.slices
				band.append((math.cos(angle) * radius, 0, -math.sin(angle) * radius))
			self.bands.append(band)

	def draw_shaded(self):
		glBegin(GL_QUAD_STRIP)
		glNormal3f(0, 1, 0)
		for i in range(self.slices):
			glVertex3f(*self.bands[1][i])
			glVertex3f(*self.bands[0][i])
		glVertex3f(*self.bands[1][0])
		glVertex3f(*self.bands[0][0])
		glEnd()
	
	def draw_edges(self):
		for band in self.bands:
			glBegin(GL_LINE_LOOP)
			for v in band:
				glVertex3f(*v)
			glEnd()

